#! /usr/bin/python
ident = '$Id: accounts.py 146 2006-08-23 01:42:44Z trevor $'
'''REX Services

Defines two classes, the server class and the service class.
Each service must have one server. The server provides the SOAP communication
interface to the REX server.
The service holds all metadata about the service.'''

import sys
import os
import re
from   xml.dom import minidom
from   xml.sax import saxutils
import common
import ConfigParser
import soap
import wx
import icon_manager

current_esm_config_version = 2
accounts = {}		# Holds the list of available service accounts
####################################################################################################
# The account class
class account:
	"""	Represents a single eas account and manages serialization and communication for it.
	"""
	def __init__( self ):
		self.name			= None
		self.window			= None
		self.username		= ''
		self.password		= ''
		self.server			= ''
		self.secure			= True
		self.connected		= False
		self.designated		= 0
		self.id				= abs( id( self ) )
		self.x				= common.default_account_x
		self.y				= common.default_account_y
		self.w				= common.default_account_w
		self.h				= common.default_account_h
		self.s1				= 300
		self.s2				= 300
		self.s3				= -300
		self.s4				= 300
		# Window sizes and positions, defaults
		self.account_manager_aid = 0

	def get_full_url( self, module='web' ):
		url = "%s/soap/%s.esp" % ( self.get_short_url(), module )
		return url

	def get_short_url( self ):
		url = "https://" + self.server
		return url

	def connect( self ):
		'''Initiates authentication with the server.'''
		result = self.execute( 'base', "user_check" )
		self.connected = False
		#print " user_check result ", result
		#print type( result )
		if result == "1":
			self.connected = True
		else:
			msg = "Invalid credentials, please check the username and password."
			dlg = wx.MessageDialog( None, msg, "Connection Failed", wx.OK )
			dlg.ShowModal()
			dlg.Destroy()
			dlg = None

	def execute( self, module, *args, **kwargs ):
		"""
		Execute a command on the connection and return the result
		This wrapper handles adding the username and password and
		what method to call.
		"""
		common.dprint( "--> Execution args on args: %s kwargs: %s " % ( args, kwargs ) )
		# Check the server settings
		if not self.username:
			common.dprint("Missing username")
			return
		if not self.password:
			common.dprint("Missing password")
			return
		# First get a valid connection
		module = module.lower()
		# for now make web serve as the base interface
		if module == 'base': module = 'web'
		url 		= self.get_full_url( module )
		namespace	= "http://tempuri.org/message/"
		encoding	= "UTF-8"
		#noroot		= 1
		#http_proxy  = ""
		#if "silver.bitbuilder.com" in url:
		#	http_proxy = ""
		#else:
		#	http_proxy  = "" #"proxy.nesma.net.sa:8080"
		#print "connecting"
		connection	= soap.SOAPProxy( url, namespace, encoding )
		#print "done trying"
		if connection is None:
			errdata = "Source: %s\nError: %s\n" % ( sys.exc_info()[0], sys.exc_info()[1] )
			msg = "Failed to obtain valid connection to the server\n%s" % errdata
			dlg = wx.MessageDialog( None, msg, "Communication Error", wx.OK )
			dlg.ShowModal()
			dlg.Destroy()
			dlg = None
			return None
		# Get the desired method
		function	= args[0]
		method_name = "connection.%s" % function
		method 		= eval( method_name )
		# Add the username and password to the arguments
		kwargs['username'] = self.username
		kwargs['password'] = self.password
		# Do we have the tracer open?
		common.soap_out	= ''
		common.soap_in	= ''
		ret 			= ''
		common.dprint("*--> Executing command on %s: %s( %s )" % ( url, method_name, kwargs ))
		#print ( kwargs )
		#ret = method( **kwargs )
		try:
			ret = method( **kwargs )
			common.dprint("RESULT: --[", ret, "]--")
		except:
			#~ errdata = "Source: %s\nError: %s\n" % ( sys.exc_info()[0], sys.exc_info()[1] )
			#~ msg = "Failed to communicate with the server.\n" + \
				  #~ "You may try to start the Tracer to help you diagnose the problem.\n\n" + errdata
			#~ dlg = wx.MessageDialog( None, msg, "Communication Error", wx.OK )
			#~ #icon = icon_manager.get_icon( "ESM" )
			#~ #dlg.SetIcon( icon )
			#~ dlg.ShowModal()
			#~ dlg.Destroy()
			#~ dlg = None
			msg = "Failed to communicate with the server.\n" + \
				"You may try to start the Tracer to help you diagnose the problem.\n\n"
			common.show_error( msg )
		# Inform the tracer
		if common.tracer:
			common.tracer.add_new_action( url, function, common.soap_out, common.soap_in )
		if type( ret ) == int:
			ret = unicode( ret )
		common.dprint( "preparing xml %s" % ( ret ) )
		# if the soap function returns a number, we may get something looking like this: {'BOGUS': '55'}
		# where 55 is the number being returned.
		numfinder = re.compile( "\{'BOGUS': '\d+'\}" );
		match = numfinder.findall( str( ret ) )
		if match:
			print "num return found: %s in %s" % (match, ret)
			numfinder = re.compile( "\d+" );
			match = numfinder.findall( str( match ) )
			match = str( match[0] )
			print "integer found: " + match
			ret = match
		if ret is None :
			common.show_error( "Your EAS server has returned false data" )
		ret = common.prepare_xml( ret )

		# Check for a <fault code="">message</fault> error
		# Display it in a box
		if ret[:6] == '<fault':
			cdom = minidom.parseString( ret )
			fault = cdom.firstChild
			if fault:
				dlg = wx.MessageDialog( None, fault.firstChild.nodeValue, "Error: " + str(fault.getAttribute('code')), wx.OK
)
				icon = icon_manager.get_icon( "ESM" )
				dlg.SetIcon( icon )
				dlg.ShowModal()
				dlg.Destroy()
				dlg = None


		return ret

	def save( self, oldname = None ):
		"""Saves the properties of the account to the conf file"""
		if oldname:
			# We had an old name for this account, delete that section first, create a new one.
			common.conf.remove_section( oldname )
			common.conf.add_section( self.name )
		common.conf.set( self.name, "server", self.server )
		common.conf.set( self.name, "username", self.username )
		common.conf.set( self.name, "password", self.password )
		try:
			common.conf.set( self.name, "designated", self.window.designated_contact_uuid )
			common.dprint( "setting designated contact as: ", self.window.designated_contact_uuid )
		except:
			common.dprint("No window exists for this account yet.")
			pass
		if self.window:
			p = self.window.GetPosition()
			s = self.window.GetSize()
			self.x = p[0]
			self.y = p[1]
			self.w = s[0]
			self.h = s[1]
			common.conf.set( self.name, "x", unicode( self.x ) )
			common.conf.set( self.name, "y", unicode( self.y ) )
			common.conf.set( self.name, "w", unicode( self.w ) )
			common.conf.set( self.name, "h", unicode( self.h ) )
			common.conf.set( self.name, "s1", unicode( self.s1 ) )
			common.conf.set( self.name, "s2", unicode( self.s2 ) )
			common.conf.set( self.name, "s3", unicode( self.s3 ) )
			common.conf.set( self.name, "s4", unicode( self.s4 ) )
		save_conf_file()


####################################################################################################
def get_conf_file():
	"""Loads up the configuration file usualyl at ~/.esm
	This file holds preferenc and account data."""
	# check that the dir exits
	if not os.path.isdir( common.conf_dir ):
		common.dprint( "Creating ", common.conf_dir )
		os.mkdir( common.conf_dir )
	common.conf = ConfigParser.ConfigParser()
	if not os.path.isfile( common.conf_filename ):
		# No config file exists, build one real quick
		common.conf.set("DEFAULT", "version", common.conf_version )
		common.conf.set("DEFAULT", "font-family", common.default_font_family )
		common.conf.set("DEFAULT", "font-size", common.default_font_size )
		common.conf.write( open( common.conf_filename, "w" ) )
	else:
		# load the defaults
		common.conf.readfp( open( common.conf_filename ) )
		# Override the defaults
		try: common.conf_version = common.conf.get	( "DEFAULT", "version" )
		except: common.conf.set("DEFAULT", "version", common.conf_version )
		try: common.default_font_family = common.conf.get	( "DEFAULT", "font-family" )
		except: common.conf.set("DEFAULT", "font-family", common.default_font_family )
		try: common.default_font_size = common.conf.get	( "DEFAULT", "font-size" )
		except: common.conf.set("DEFAULT", "font-size", common.default_font_size )
		try: 
			firstrun = common.conf.get	( "DEFAULT", "firstrun" )
			if firstrun == 'f': common.firstrun = False
			else : common.dprint( "first run is : " + firstrun )
		except: 
			if common.firstrun: common.conf.set("DEFAULT", "firstrun", 't' )
			else: common.conf.set( "DEFAULT", "firstrun", 'f' )
	return common.conf

def save_conf_file():
	if common.firstrun: 
		common.conf.set( "DEFAULT", "firstrun", 't' )
		common.dprint( "setting firstrun to t" )
	else: 
		common.conf.set( "DEFAULT", "firstrun", 'f' )
		common.dprint( "setting firstrun to f" )
	common.conf.write( open( common.conf_filename, "w" ) )

# Module functions
def get_service( service_name ):
	'Returns a service object by name'
	svc = None
	for service_object in accounts:
		if service_object.name == service_name:
			svc = service_object
			break
	return svc

def create_account( name, server, username, password ):
	'Creates and saves a new account'
	global accounts
	# First we check if all the variables are there
	try:
		if len( name ) < 1		: raise
		if len( server ) < 1	: raise
		if len( username ) < 1	: raise
		if len( password ) < 1	: raise
	except Exception:
		common.dprint( "Account missing value, skipping." )
		return None
	# Next we try to find the service, if it already exists (by name) we skip
	if common.conf.has_section( name ):
		common.dprint( "An account named ", name, " already exists." )
		return None
	acct = account()
	# set some defaults
	acct.name		= name
	acct.server		= server
	acct.secure		= True
	acct.username	= username
	acct.password	= password
	acct.x			= 0
	acct.y			= 0
	acct.w			= 800
	acct.h			= 600
	acct.s1			= 300
	acct.s2			= 300
	acct.s3			= -300
	#acct.s4			= 300
	# create the conf file side
	common.conf.add_section( acct.name )
	# save the account
	acct.save()
	accounts[ abs( id( acct ) ) ] = acct
	return acct

def load_accounts():
	'Loads the list of accounts'
	global accounts
	accounts = {}
	common.conf = get_conf_file()
	# Now load up the accounts
	for account_section in common.conf.sections():
		#print "Loading section", account_section
		acct = account()
		# set some defaults
		acct.name		= account_section
		acct.server		= 'localhost'
		acct.secure		= True
		acct.username	= ''
		acct.password	= ''
		acct.x			= 0
		acct.y			= 0
		acct.w			= 800
		acct.h			= 600
		acct.s1			= 300
		acct.s2			= 300
		acct.s3			= -300
		acct.s4			= 300
		# read the important values
		if common.conf.has_option( account_section, 'server'):
			acct.server = common.conf.get( account_section, 'server' )
		if common.conf.has_option( account_section, 'username'):
			acct.username = common.conf.get( account_section, 'username' )
		if common.conf.has_option( account_section, 'password'):
			acct.password = common.conf.get( account_section, 'password' )
		if common.conf.has_option( account_section, 'designated'):
			acct.designated = common.conf.get( account_section, 'designated' )
		if common.conf.has_option( account_section, 'x'):
			x = common.conf.getint( account_section, 'x' )
			if x < 0: x = 0
			acct.x = x
		if common.conf.has_option( account_section, 'y'):
			y = common.conf.getint( account_section, 'y' )
			if y < 0: y = 0
			acct.y = y
		if common.conf.has_option( account_section, 'w'):
			w = common.conf.getint( account_section, 'w' )
			if w < 400: w = 800
			acct.w = w
		if common.conf.has_option( account_section, 'h'):
			h = common.conf.getint( account_section, 'h' )
			if h < 400: h = 600
			acct.h = h
		if common.conf.has_option( account_section, 's1'):
			s1 = common.conf.getint( account_section, 's1' )
			acct.s1 = s1
		if common.conf.has_option( account_section, 's2'):
			s2 = common.conf.getint( account_section, 's2' )
			acct.s2 = s2
		if common.conf.has_option( account_section, 's3'):
			s3 = common.conf.getint( account_section, 's3' )
			acct.s3 = s3
		if common.conf.has_option( account_section, 's4'):
			s4 = common.conf.getint( account_section, 's4' )
			acct.s4 = s4
		#print abs( id( acct ) ), type( id( acct ) ), dir( acct )
		accounts[ abs( id( acct ) ) ] = acct
	save_conf_file()
	return True

def delete_account( account_id ):
	'Deletes a service object and data by name'
	account = accounts[ account_id ]
	common.conf.remove_section( account.name )
	del account
	del accounts[ account_id ]
	# Save
	save_conf_file()
